/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.ElevatorPlanStrategy;
import org.springblade.modules.admin.entity.MaintainPlanConfig;
import org.springblade.modules.admin.enums.ElevatorMaintainStatusEnums;
import org.springblade.modules.admin.enums.TaskStatusEnum;
import org.springblade.modules.admin.service.IMaintainPlanConfigService;
import org.springblade.modules.admin.service.IMaintainPlanContentExtService;
import org.springblade.modules.admin.vo.ElevatorPlanStrategyVO;
import org.springblade.modules.admin.mapper.ElevatorPlanStrategyMapper;
import org.springblade.modules.admin.service.IElevatorPlanStrategyService;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.admin.vo.MaintainPlanContentExtVO;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 电梯保养计划策略 服务实现类
 *
 * @author ysq
 * @since 2023-04-25
 */
@AllArgsConstructor
@Service
public class ElevatorPlanStrategyServiceImpl extends BaseServiceImpl<ElevatorPlanStrategyMapper, ElevatorPlanStrategy> implements IElevatorPlanStrategyService {

	private final IMaintainPlanConfigService maintainPlanConfigService;
	private final IMaintainPlanContentExtService maintainPlanContentExtService;

	@Override
	public IPage<ElevatorPlanStrategyVO> selectElevatorPlanStrategyPage(IPage<ElevatorPlanStrategyVO> page, ElevatorPlanStrategyVO elevatorPlanStrategy) {
		return page.setRecords(baseMapper.selectElevatorPlanStrategyPage(page, elevatorPlanStrategy));
	}

	@Override
	public ElevatorPlanStrategy getByElevatorId(Long elevatorId) {
		ElevatorPlanStrategy planStrategy = this.getOne(Wrappers.<ElevatorPlanStrategy>lambdaQuery().eq(ElevatorPlanStrategy :: getElevatorId, elevatorId), false);
		return planStrategy;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void planStrategyUpdate(List<Long> elevatorIds) {
		this.planStrategyUpdate(elevatorIds, false);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void planStrategyUpdate(List<Long> elevatorIds, boolean isUpdateOverdueDate) {
		if (Func.isEmpty(elevatorIds)) {
			return;
		}

		List<ElevatorPlanStrategy> planStrategyList = new ArrayList<>();
		for (Long elevatorId : elevatorIds) {
			ElevatorPlanStrategy strategy = this.getByElevatorId(elevatorId);
			List<MaintainPlanContentExtVO> planContentList = maintainPlanContentExtService.maintainPlanList(elevatorId);
			if (Func.isEmpty(planContentList)) {
				if (Func.isNotEmpty(strategy)) {
					strategy.setNextPlanId(null);
					strategy.setNextPlanDate(null);
					strategy.setOverdueDate(null);
					strategy.setOtherOverdue(null);
					strategy.setLastDate(null);
					this.updateById(strategy);
				}
				continue;
			}

			// 已完成的工单
			List<MaintainPlanContentExtVO> finishPlanList = planContentList.stream()
				.filter(t -> t.getPlanStatus() == TaskStatusEnum.BY_MAINTAIN_SIGN.getStatus() || t.getPlanStatus() == TaskStatusEnum.BY_SIGN.getStatus())
				.sorted(Comparator.comparing(MaintainPlanContentExtVO :: getMaintainSignatureTime).reversed()).collect(Collectors.toList());

			// 未完成的工单
			List<Integer> notStartStatus = Arrays.asList(TaskStatusEnum.BY_DISABLE.getStatus(), TaskStatusEnum.BY_PROCESSED.getStatus(), TaskStatusEnum.BY_FINISH.getStatus());
			List<MaintainPlanContentExtVO> notStartPlanList = planContentList.stream()
				.filter(t -> notStartStatus.contains(t.getPlanStatus()))
				.sorted(Comparator.comparing(MaintainPlanContentExtVO :: getPlanDate)).collect(Collectors.toList());

			if (Func.isEmpty(strategy)) {
				strategy = new ElevatorPlanStrategy();
			}
			strategy.setOtherOverdue(this.otherOverdueHandle(notStartPlanList));
			// 最末次保养计划日期
			Date lastDate = planContentList.stream().map(t -> t.getPlanDate()).max(Comparator.comparing(Date::getTime)).get();
			strategy.setLastDate(lastDate);

			/**
			 * 下次保养计划-逾期风险判断规则：
			 * 下次保养计划的日期，如果与上次维保签字日期间隔超15天以上（在当前逾期临界日期之后），则视为存在逾期风险。
			 */
			// 首保未开始
			if (Func.isEmpty(finishPlanList)) {
				MaintainPlanContentExtVO nextPlan = notStartPlanList.get(0);
				strategy.setElevatorId(nextPlan.getElevatorId());
				strategy.setNextPlanId(nextPlan.getPlanId());
				strategy.setNextPlanDate(nextPlan.getPlanDate());
				strategy.setIsFirst(0);
				strategy.setOverdueDate(DateUtil.offsetDay(nextPlan.getPlanDate(), 15));
				strategy.setTenantId(nextPlan.getTenantId());
				planStrategyList.add(strategy);
				continue;
			}

			// 工单已全部做完
			if (Func.isEmpty(notStartPlanList)) {
				MaintainPlanContentExtVO previousPlan = finishPlanList.get(0);
				strategy.setElevatorId(previousPlan.getElevatorId());
				strategy.setPreviousPlanId(previousPlan.getPlanId());
				String signatureTime = DateUtil.format(previousPlan.getMaintainSignatureTime(), "yyyy-MM-dd");
				strategy.setPreviousPlanDate(DateUtil.parseDate(signatureTime));
				strategy.setNextPlanId(null);
				strategy.setNextPlanDate(null);
				strategy.setIsFirst(1);
				if (isUpdateOverdueDate) {
					strategy.setOverdueDate(DateUtil.offsetDay(DateUtil.parseDate(signatureTime), 15));
				}
				strategy.setTenantId(previousPlan.getTenantId());
				planStrategyList.add(strategy);
				continue;
			}

			// 上次保养计划
			MaintainPlanContentExtVO plan1 = finishPlanList.get(0);
			Date signatureTime = DateUtil.parseDate(DateUtil.format(plan1.getMaintainSignatureTime(), "yyyy-MM-dd"));
			// 未来保养计划排期
			List<MaintainPlanContentExtVO> featurePlanList = notStartPlanList.stream().filter(t -> DateUtil.compare(t.getPlanDate(), signatureTime) >= 0)
				.sorted(Comparator.comparing(MaintainPlanContentExtVO :: getPlanDate)).collect(Collectors.toList());
			strategy.setElevatorId(elevatorId);
			strategy.setPreviousPlanId(plan1.getPlanId());
			strategy.setPreviousPlanDate(signatureTime);
			if (Func.isEmpty(featurePlanList)) {
				strategy.setNextPlanId(null);
				strategy.setNextPlanDate(null);
			} else {
				MaintainPlanContentExtVO plan2 = featurePlanList.get(0);
				strategy.setNextPlanId(plan2.getPlanId());
				strategy.setNextPlanDate(plan2.getPlanDate());
			}
			strategy.setIsFirst(1);
			if (isUpdateOverdueDate) {
				strategy.setOverdueDate(DateUtil.offsetDay(signatureTime, 15));
			}
			strategy.setTenantId(plan1.getTenantId());
			planStrategyList.add(strategy);
		}

		this.saveOrUpdateBatch(planStrategyList);
	}

	/**
	 * 其他剩余计划-逾期风险判断规则：
	 * 除下一次保养计划外，若当前时间往后的剩余排期计划中，存在与前一个排期计划间隔超15天的情况，则视为存在逾期风险。
	 * @param notStartPlanList
	 * @return
	 */
	private Integer otherOverdueHandle(List<MaintainPlanContentExtVO> notStartPlanList) {
		if (notStartPlanList.size() <= 1) {
			return null;
		}

		// 未来保养计划排期
		Date today = DateUtil.parseDate(DateUtil.format(DateUtil.date(), "yyyy-MM-dd"));
		List<MaintainPlanContentExtVO> featurePlanList = notStartPlanList.stream().filter(t -> DateUtil.compare(t.getPlanDate(), today) >= 0)
			.sorted(Comparator.comparing(MaintainPlanContentExtVO :: getPlanDate)).collect(Collectors.toList());
		if (Func.isEmpty(featurePlanList)) {
			return null;
		}

		MaintainPlanContentExtVO nextPlan = featurePlanList.get(0);
		// 在保状态
		List<Integer> maintainStatusList = Arrays.asList(ElevatorMaintainStatusEnums.CONTRACT_IN_TIME.getId(), ElevatorMaintainStatusEnums.CONTRACT_OUT_TIME.getId(), ElevatorMaintainStatusEnums.DIS_ENABLE.getId());
		// 只处理电梯在保且为年审梯的工单
		boolean checkTag = !maintainStatusList.contains(nextPlan.getMaintainStatus()) || !Func.equalsSafe(nextPlan.getElevatorUseType(), 0);
		if (checkTag) {
			return null;
		}

		Integer otherOverdue = 0;
		Date firstDate = nextPlan.getPlanDate();
		for (int i = 1; i < featurePlanList.size(); i++) {
			MaintainPlanContentExtVO notStartPlan = featurePlanList.get(i);
			if (DateUtil.betweenDay(firstDate, notStartPlan.getPlanDate(), true) > 15) {
				otherOverdue = 1;
				break;
			} else {
				firstDate = notStartPlan.getPlanDate();
			}
		}
		return otherOverdue;
	}

	@Override
	public void script() {
		List<MaintainPlanConfig> configList = maintainPlanConfigService.list();
		List<Long> elevatorIds = configList.stream().map(t -> t.getElevatorId()).collect(Collectors.toList());
		this.planStrategyUpdate(elevatorIds);
	}

	@Override
	public boolean updateOverdueDate(ElevatorPlanStrategy elevatorPlanStrategy) {
		if (Func.isEmpty(elevatorPlanStrategy.getId()) || Func.isEmpty(elevatorPlanStrategy.getOverdueDate())) {
			throw new ServiceException("参数非法！");
		}

		ElevatorPlanStrategy strategy = baseMapper.selectById(elevatorPlanStrategy.getId());
		if (Func.isEmpty(strategy)) {
			throw new ServiceException("电梯保养计划策略不存在！");
		}

		// 电梯逾期临界日期
		Date overdueDate = strategy.getIsFirst() == 0 ? DateUtil.offsetDay(strategy.getNextPlanDate(), 15) : DateUtil.offsetDay(strategy.getPreviousPlanDate(), 15);
		if (DateUtil.compare(new Date(), DateUtil.offsetDay(overdueDate, 15)) <= 0) {
			throw new ServiceException("【上次维保签字日期】离今日间隔超30天才允许手动设置逾期临界日期！");
		}
		if (DateUtil.compare(elevatorPlanStrategy.getOverdueDate(), overdueDate, "yyyy-MM-dd") < 0) {
			throw new ServiceException("仅允许选择【上次维保签字日期+15天】或之后的日期！");
		}

		strategy.setOverdueDate(elevatorPlanStrategy.getOverdueDate());
		return baseMapper.updateById(strategy) > 0;
	}
}
